package com.istock.monitor.agent.sql;

import java.net.URL;
import java.sql.Array;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Struct;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class MySql {
	
	private String sql;
	private List<String> batchSqls;
	
	
	// Use for PreparedStatment and CallableStatement.
	private int parameterCount;
	// Object type supports only Primitive Type, Number, Date and String,
	// And use for PreparedStatment and CallableStatement.
	private Object[] parameters;
	
	// Use for CallableStatement only.
	private Map<String, Object> parameterMap;
	
	private transient int batchIndex;
	private List<Object[]> batchParameters;
	
	private long processTime;
	
	private boolean hasTransaction;
	// Do not use isSuccess in case of falling into (de)serialization trap.
	private boolean success;

	public String getSql() {
		return sql;
	}

	public void setSql(String sql) {
		this.sql = sql;
	}

	public List<String> getBatchSqls() {
		return batchSqls;
	}

	public void setBatchSqls(List<String> batchSqls) {
		this.batchSqls = batchSqls;
	}

	public void addBatchSql(String sql) {
		if (sql != null) {
			if (this.batchSqls == null) {
				this.batchSqls = new ArrayList<String>();
			}
			this.batchSqls.add(sql);
		}
	}

	int getParameterCount() {
		return parameterCount;
	}

	void setParameterCount(int parameterCount) {
		this.parameterCount = parameterCount;
	}

	public Object[] getParameters() {
		return parameters;
	}

	public void setParameters(Object[] parameters) {
		this.parameters = parameters;
	}
	
	public void setParameter(int index, Object parameter, int sqlType) {
		if (parameters == null) {
			parameters = new Object[parameterCount];
		}
		parameters[index-1] = format(parameter, sqlType);
	}

	public Map<String, Object> getParameterMap() {
		return parameterMap;
	}

	public void setParameterMap(Map<String, Object> parameterMap) {
		this.parameterMap = parameterMap;
	}
	
	public void putParameter(String name, Object value, int sqlType) {
		if (this.parameterMap == null) {
			this.parameterMap = new HashMap<String, Object>();
		}
		this.parameterMap.put(name, format(value, sqlType));
	}
	
	static Object format(Object parameter, int sqlType) {
		if (parameter == null) return null;
		if (parameter instanceof Boolean
				|| parameter instanceof Character
				|| parameter instanceof Number
				|| parameter instanceof String) {
			return parameter;
		}
		
		if (parameter instanceof java.util.Date 
				|| parameter instanceof java.util.Calendar) {
			SimpleDateFormat formatter = null;
			if (parameter instanceof java.sql.Date) {
				formatter = new SimpleDateFormat("yyyy-MM-dd");
			} else if (parameter instanceof java.sql.Time) {
				formatter = new SimpleDateFormat("HH:mm:ss");
			} else {// if (parameter instanceof java.sql.Timestamp)
				formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
			}
			if (parameter instanceof java.util.Calendar) {
				return formatter.format(((java.util.Calendar) parameter).getTime());
			}
			return formatter.format((java.util.Date) parameter);
		}
		
		if (parameter instanceof URL) {// DataLink (for Oracle)
			return ((URL)parameter).toExternalForm();
		}
		if (parameter instanceof RowId) {// RowId (for Oracle)
			return ((RowId)parameter).toString();
		}
		if (parameter instanceof SQLXML) {// SQLXML
			try {
			return ((SQLXML)parameter).getString();
			} catch (SQLException e) {return "<SQLXML>";}
		}
		if (parameter instanceof Array) {// Array
			return "<ARRAY>";
		}
		if (parameter instanceof Struct) {// Struct
			return "<STRUCT>";
		}
		
		// Other Types, Not serialize them.
		if (sqlType == Types.BINARY) {
			return "<BINARY>";
		}
		if (sqlType == Types.VARBINARY 
				|| sqlType == Types.LONGVARBINARY) {
			return "<VARBINARY>";
		}
		if (sqlType == Types.LONGVARCHAR
				|| sqlType == Types.LONGNVARCHAR) {
			return "<LONGVARCHAR>";
		}
		if (sqlType == Types.BLOB) {
			return "<BLOB>";
		}
		if (sqlType == Types.CLOB 
				|| sqlType == Types.NCLOB) {
			return "<CLOB>";
		}
		
		return "<OBJECT>";
	}

	public List<Object[]> getBatchParameters() {
		return batchParameters;
	}

	public void setBatchParameters(List<Object[]> batchParameters) {
		this.batchParameters = batchParameters;
	}
	
	void clearSql() {
		this.sql = null;
	}
	
	void addBatch() {
		if (batchParameters == null) {
			batchParameters = new ArrayList<Object[]>();
		}
		if (batchIndex >= batchParameters.size()) {
			batchParameters.add(parameters);
			batchIndex++;
		}
		clearParameters();
	}
	
	void clearBatch() {
		batchIndex = 0;
		if (batchParameters != null) {
			batchParameters.clear();
		}
		batchParameters = null;
	}
	
	void clearBatchSqls() {
		if (batchSqls != null) {
			batchSqls.clear();
		}
		batchSqls = null;
	}
	
	void clearParameters() {
		parameters = null;
		if (parameterMap != null) {
			parameterMap.clear();
		}
		parameterMap = null;
	}
	
	public long getProcessTime() {
		return processTime;
	}

	public void setProcessTime(long processTime) {
		this.processTime = processTime;
	}

	public boolean isHasTransaction() {
		return hasTransaction;
	}

	public void setHasTransaction(boolean hasTransaction) {
		this.hasTransaction = hasTransaction;
	}

	public boolean isSuccess() {
		return success;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}

}
